#pragma once

#include <list>
#include <stdexcept>
#include <algorithm>

#include "ListProxy.hpp"

namespace cge {
	template< class Listener >
	class BaseSource {
	public:
		BaseSource() {}
		virtual ~BaseSource() {
			removeDead();
			for(auto i=ls_.begin(); i!=ls_.end(); ++i)
				if(*i) (*i)->s_ = 0;
		}

	protected:
		bool hasListeners() const { return !ls_.empty(); }

		void addListener ( Listener& l ) {
			if( l.s_ ) {
				if( l.s_ == this ) {
					if( !l.dead_ )
						throw std::logic_error("BaseSource: Duplicate listener addition.");
					else {
						l.dead_ = false;
						dead_.remove(l.it_);
					}
				} else
					throw std::logic_error("BaseSource: Adding of a listener belonging to a different source attempted.");
			} else {
				ls_.push_front(&l);
				l.it_ = ls_.begin();
				l.s_  = this;
			}
		}

		void removeListener( Listener& l ) {
			if( !l.s_ )
				throw std::logic_error("BaseSource: Unbound listener removal attempted.");
			else if( l.s_ != this )
				throw std::logic_error("BaseSource: Removal of a listener belonging to a different source attempted.");
			else if( l.dead_ )
				return;

			l.dead_ = true;
			dead_.push_back(l.it_);
		}

		template<class T>
		void inject( T handler ) {
			removeDead();
			for(auto i=ls_.begin(); i!=ls_.end(); ++i)
				((*i)->*handler)();
			removeDead();
		}
		
		template<class T, class Arg1>
		void inject( T handler, Arg1 arg1 ) {
			removeDead();
			for(auto i=ls_.begin(); i!=ls_.end(); ++i)
				((*i)->*handler)(arg1);
			removeDead();
		}
		
		template<class T, class Arg1, class Arg2>
		void inject( T handler, Arg1 arg1, Arg2 arg2 ) {
			removeDead();
			for(auto i=ls_.begin(); i!=ls_.end(); ++i)
				((*i)->*handler)(arg1,arg2)
			removeDead();
		}

	private:
		template<class D>
		friend class BaseListener;

		BaseSource(const BaseSource&);

		void removeDead() {
			while( !dead_.empty() ) {
				auto l = dead_.front();
				dead_.pop_front();
				if( (*l) ) {
					if( !(*l)->dead_ )
						throw std::logic_error("BaseSource: Undead listener.");

					(*l)->s_    = 0;
					(*l)->dead_ = false;
				}
				ls_.erase( l );
			}
		}

		std::list<Listener*>  ls_;
		std::list< typename std::list<Listener*>::iterator >  dead_;
	};
}